#pragma once
#include "beanstalk.h"
#include "bspool.h"
#include <string>

class Job {
public:
	Job() {
		bs_init_job(&job_);
	}

	~Job() {
		bs_deinit_job(&job_);
	}

	int64_t id() {
		return job_.id;
	}

	std::string data() {
		if (!job_.data)
			return "";
		else
			return std::string(job_.data, job_.size);
	}

private:
	friend class Client;
	BSJ job_;
};

class ClientPool;

class Client {
public:
	Client() 
		:fd_(-1), pool_(NULL){
	}

	Client(int fd,ClientPool* pool)
		:fd_(fd), pool_(pool) {
	}

	~Client() {
		close();
	}

	int connect(const char *host, int port) {
		int rslt = bs_connect(host, port);
		if (rslt > 0) {
			fd_ = rslt;
			return 0;
		} else {
			return rslt;
		}
	}

	int connect(const char *host, int port,float timeout) {
		int rslt = bs_connect_with_timeout(host, port, timeout);
		if (rslt > 0) {
			fd_ = rslt;
			return 0;
		}
		else {
			return rslt;
		}
	}

	int64_t put(uint32_t priority, uint32_t delay, uint32_t ttr, const std::string& data) {
		return bs_put(fd_, priority, delay, ttr, data.data(), data.length());
	}

	int call(uint32_t priority, uint32_t timeout, uint32_t ttr, const std::string& data, Job *job) {
		return bs_call(fd_, priority, timeout, ttr, data.data(), data.length(), &job->job_);
	}

	int reply(int64_t job, const std::string& data) {
		return bs_reply(fd_, job, data.data(), data.length());
	}

	int ping() {
		return bs_ping(fd_);
	}

	int close() {
		if (fd_ > 0) {
			if (pool_)
				bsp_close(&pool_->pool_, fd_);
			else
				bs_close(fd_);

			pool_ = NULL;
			fd_ = -1;
		}
		return BS_STATUS_OK;
	}

	int use_tube(const char *tube) {
		return bs_use(fd_, tube);
	}

	int watch_tube(const char *tube) {
		return bs_watch(fd_, tube);
	}

	int ignore_tube(const char *tube) {
		return bs_ignore(fd_, tube);
	}

	int delete_job(int64_t job) {
		return bs_delete(fd_, job);
	}

	int reserve(Job *job) {
		return bs_reserve(fd_, &job->job_);
	}

	int reserve(Job *job,int timeout) {
		return bs_reserve_with_timeout(fd_, timeout, &job->job_);
	}

	int release(int64_t id, uint32_t priority, uint32_t delay) {
		return bs_release(fd_, id, priority, delay);
	}

	int bury(int64_t id, uint32_t priority) {
		return bs_bury(fd_, id, priority);
	}

	int touch(int64_t id) {
		return bs_touch(fd_, id);
	}

	int peek(int fd, int64_t id, Job *job) {
		return bs_peek(fd_, id, &job->job_);
	}

	int peek_ready(int fd, Job *job) {
		return bs_peek_ready(fd_, &job->job_);
	}

	int peek_delayed(int fd, Job *job) {
		return bs_peek_delayed(fd_, &job->job_);
	}

	int peek_buried(int fd, Job *job) {
		return bs_peek_buried(fd_, &job->job_);
	}

	int kick(int bound) {
		return bs_kick(fd_, bound);
	}

	std::string list_tube_used() {
		char* tube = NULL;
		std::string rslt;
		if (bs_list_tube_used(fd_, &tube) == BS_STATUS_OK) {
			rslt = tube;
			free(tube);
		}
		return rslt;
	}

	std::string list_tubes() {
		char* tube = NULL;
		std::string rslt;
		if (bs_list_tubes(fd_, &tube) == BS_STATUS_OK) {
			rslt = tube;
			free(tube);
		}
		return rslt;
	}

	std::string list_tubes_watched() {
		char* tube = NULL;
		std::string rslt;
		if (bs_list_tubes_watched(fd_, &tube) == BS_STATUS_OK) {
			rslt = tube;
			free(tube);
		}
		return rslt;
	}


	std::string stats() {
		char* stat = NULL;
		std::string rslt;
		if (bs_stats(fd_, &stat) == BS_STATUS_OK) {
			rslt = stat;
			free(stat);
		}
		return rslt;
	}

	std::string stats_job(int64_t id) {
		char* stat = NULL;
		std::string rslt;
		if (bs_stats_job(fd_,id,&stat) == BS_STATUS_OK) {
			rslt = stat;
			free(stat);
		}
		return rslt;
	}

	std::string stats_tube(const char *tube) {
		char* stat = NULL;
		std::string rslt;
		if (bs_stats_tube(fd_, tube, &stat) == BS_STATUS_OK) {
			rslt = stat;
			free(stat);
		}
		return rslt;
	}

private:
	int fd_;
	ClientPool* pool_;
};

class ClientPool {
public:
	ClientPool() {
		bsp_init(&pool_);
	}

	~ClientPool() {
		bsp_deinit(&pool_);
	}

	void set_server(const char* host, int port) {
		host_ = host;
		port_ = port;
	}

	Client* create() {
		int fd = bsp_connect(&pool_, host_.c_str(), port_);
		if (fd > 0) {
			return new Client(fd, this);
		}
		else {
			return NULL;
		}
	}

	Client* create(float secs) {
		int fd = bsp_connect_with_timeout(&pool_, host_.c_str(), port_, secs);
		if (fd > 0) {
			return new Client(fd, this);
		}
		else {
			return NULL;
		}
	}

	void destroy() {
		bsp_deinit(&pool_);
	}
private:
	friend class Client;

	std::string host_;
	int port_;
	bspool_t pool_;
};

